package com.example.adminframework.common.exception;


import com.example.adminframework.constant.ErrorCode;
import com.example.adminframework.constant.HttpCode;
import com.example.adminframework.response.ResultContent;
import org.apache.ibatis.exceptions.PersistenceException;
import org.mybatis.spring.MyBatisSystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Date 2020.12.02
 * @Description 全局异常处理 分为全局自定义异常和 全局其他异常处理
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理请求类的属性注解验证
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
        //获取表单验证异常
        BindingResult result = ex.getBindingResult();
        //组装校验错误信息
        Map<String, List<Map<String, String>>> map = new HashMap<>();
        if (result.hasErrors()) {
            //获取全部异常信息
            List<ObjectError> list = result.getAllErrors();
            for (ObjectError error : list) {
                if (error instanceof FieldError) {
                    FieldError fieldError = (FieldError) error;
                    //把验证错误信息分组封装
                    this.packagingInformation(map, fieldError);
                }
            }
        }
        log.error(ex.getMessage(), ex);
        return ResponseEntity.ok(ResultContent.error(HttpCode.BAD_REQUEST, map));
    }

    /**
     * 把验证错误信息封装到map中
     */
    private void packagingInformation(Map<String, List<Map<String, String>>> map, FieldError fieldError) {
        //获取验证错误属性名
        String field = fieldError.getField();
        //获取验证错误信息
        String defaultMessage = fieldError.getDefaultMessage();
        //获取规则注解名称
        String code = fieldError.getCode();
        //判断是否已有这个filed
        if (map.containsKey(field)) {
            Map<String, String> fieldMap = new HashMap<>();
            fieldMap.put("rule", code);
            fieldMap.put("message", defaultMessage);
            map.get(field).add(fieldMap);
        } else {
            List<Map<String, String>> fieldList = new ArrayList<>();
            Map<String, String> fieldMap = new HashMap<>();
            fieldMap.put("rule", code);
            fieldMap.put("message", defaultMessage);
            fieldList.add(fieldMap);
            map.put(field, fieldList);
        }
    }

    /**
     * 处理controller的验证注解
     *
     * @param ex ex
     */
//    @ExceptionHandler(ConstraintViolationException.class)
//    public ResultContent<Object> handleConstraintViolationException(ConstraintViolationException ex) {
//        log.error(ex.getMessage(), ex);
//        return ResultContent.error(HttpCode.BAD_REQUEST, ex.getLocalizedMessage());
//    }

    /**
     * 处理验证异常
     *
     * @param ex 验证异常
     */
    @ExceptionHandler(ValidationException.class)
    public ResultContent<Object> handleValidationException(ValidationException ex) {
        log.error(ex.getMessage(), ex);
        return ResultContent.error(ex.getHttpCode(), ex.getData());
    }

    /**
     * 全局自定义异常
     */
    @ExceptionHandler(value = CommonException.class)
    public ResultContent GlobalCommonExceptionHandler(HttpServletRequest req, CommonException e) {
        log.error(e.getMessage(), e);
        return ResultContent.error(e.getHttpCode(), e.getErrorCode());
    }

    /**
     * 全局其他异常
     */
    @ExceptionHandler(value = MyBatisSystemException.class)
    public ResultContent GlobalMyBatisSystemExceptionHandler(HttpServletRequest req, Exception e) {
        if (e != null && e.getCause() instanceof PersistenceException && e.getCause().getCause() instanceof CommonException)
            return GlobalCommonExceptionHandler(req, (CommonException) e.getCause().getCause());
        
        return GlobalOtherExceptionHandler(req, e);
    }

    /**
     * 全局其他异常
     */
    @ExceptionHandler(value = Exception.class)
    public ResultContent GlobalOtherExceptionHandler(HttpServletRequest req, Exception e) {
        log.error(e.getMessage(), e);
        return ResultContent.error(HttpCode.INTERNAL_SERVER_ERROR, ErrorCode.UN_KNOWN_ERROR);
    }
}
